package com.mvc.app.implementations;

import com.container.app.annotations.Component;
import com.mvc.app.ActionRequest;
import com.mvc.app.Dispatcher;
import com.mvc.app.Model;
import com.mvc.app.ModelView;
import com.mvc.app.exceptions.InvalidControllerMethodException;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import static com.mvc.app.utils.MVCUtils.*;

/**
 * Created with IntelliJ IDEA.
 * User: Georgiana.Scarlat
 * Date: 8/29/12
 * Time: 9:49 AM
 */
@Component
public class DispatcherImpl implements Dispatcher {


    public DispatcherImpl() {
    }

    public ModelView dispatchToController(ActionRequest action, Model model) {

        Method controllerMethod = action.getControllerMethod();

        validateActionRequest(action);

        try {
            return (ModelView) controllerMethod.invoke(action.getController(), supplyWantedParametersFromModel(action, model));
        } catch (IllegalAccessException e) {
            throw new InvalidControllerMethodException("Can't access controller method " + controllerMethod.getName(), e);
        } catch (InvocationTargetException e) {
            throw new InvalidControllerMethodException("UserController method " + controllerMethod.getName() + " throws an exception", e);
        }
    }


    private Object[] supplyWantedParametersFromModel(ActionRequest action, Model model) {
        ArrayList<Object> parameters = new ArrayList<Object>();
        Annotation[][] parameterAnnotations = action.getControllerMethod().getParameterAnnotations();

        addRequestAndResponseParams(action, parameters);

        if (parameterAnnotations.length > 2)
            validateNonEmptyModel(model);

        for (int i = 2; i < parameterAnnotations.length; i++) {
            processParameterAnnotations(model, parameters, parameterAnnotations[i]);
        }

        return parameters.toArray();
    }

    private void addRequestAndResponseParams(ActionRequest action, ArrayList<Object> parameters) {
        parameters.add(action.getRequest());
        parameters.add(action.getResponse());
    }

    private void processParameterAnnotations(Model model, ArrayList<Object> parameters, Annotation[] parameterAnnotations) {


        if (parameterAnnotations.length == 0)
            throw new InvalidControllerMethodException("UserController method has no parameter annotation.");

        for (Annotation annotation : parameterAnnotations) {

            if (!addParameter(model, parameters, annotation))
                throw new InvalidControllerMethodException("UserController method has only unknown parameter annotation.");
        }
    }

    private boolean addParameter(Model model, ArrayList<Object> parameters, Annotation annotation) {

        if (isBeanOrPropertyAnnotation(annotation)) {
            String annotationNameValue = getAnnotationNameValue(annotation);
            parameters.add(model.getModelElement(annotationNameValue, annotation.annotationType()));
            return true;
        }
        return false;
    }


}
